# -*- coding: utf-8 -*-
# @Time    : 2024/6/26 15:22
# @Author  : yujiahao
# @File    : 16_pandas_chart.py
# @description:Python Pandas绘图


'''

Pandas 在数据分析、数据可视化方面有着较为广泛的应用，Pandas 对 Matplotlib 绘图软件包的基础上单独封装了一个plot()接口，
通过调用该接口可以实现常用的绘图操作。

'''

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt


# todo 1、看个例子
def pandas_cahrt_demo():
    '''

    如下图所示，如果行索引中包含日期，Pandas 会自动调用 gct().autofmt_xdate() 来格式化 x 轴。

        除了使用默认的线条绘图外，您还可以使用其他绘图方式，如下所示：
        柱状图：bar() 或 barh()
        直方图：hist()
        箱状箱：box()
        区域图：area()
        散点图：scatter()
    '''
    # 创建包含时间序列的数据
    df = pd.DataFrame(np.random.randn(8, 4), index=pd.date_range('2/1/2020', periods=8), columns=list('ABCD'))

    # 绘制图表
    df.plot()

    # 显示图表
    plt.show()


#  todo 2、柱状图
def pandas_bar():
    # 通过关键字参数kind可以把上述方法传递给 plot()。横轴是索引，纵轴是数值，

    df = pd.DataFrame(np.random.rand(10, 5), columns=['a', 'b', 'c', 'd', 'e'])
    print(df)

    # 默认的绘图类型是折线图
    df.plot()
    # 或使用df.plot(kind="bar")
    df.plot.bar()
    # 通过设置参数stacked = True可以生成柱状堆叠图，
    df.plot(kind='bar', stacked=True)
    # 绘制水平柱状图
    df.plot.barh(stacked=True)

    plt.show()


# todo 3、直方图
def pandas_hist():
    ''' plot.hist() 可以实现绘制直方图，并且它还可以指定 bins（构成直方图的箱数）。
    是单独给每一列生成直方图的

        【参数说明】
            alpha：设置透明度，值在 0 到 1 之间。0 为完全透明，1 为完全不透明。
            bins：设置直方图的柱数（bins），即数据分组的数量。
    '''

    # 生成包含 1000 行 4 列随机数的 DataFrame
    df = pd.DataFrame(np.random.randn(1000, 4), columns=['a', 'b', 'c', 'd'])

    print(df)

    # 生成直方图
    ax = df.plot.hist(alpha=1, bins=30)

    # 保存图表为文件
    # plt.savefig('histogram.png')

    # 计算差分并绘制直方图
    df = pd.DataFrame({'A': np.random.randn(100) + 2, 'B': np.random.randn(100), 'C':
        np.random.randn(100) - 2, 'D': np.random.randn(100) + 3}, columns=['A', 'B', 'C', 'D'])
    # 使用diff绘制

    '''diff 是一个用于计算数据差分的函数。在 Pandas 中，DataFrame.diff() 方法用于计算数据框中每一列的元素之间的差值。
        具体来说，它计算的是当前行与前一行之间的差值。
    '''
    df.diff().hist(color="r", alpha=0.5, bins=15)

    # 显示图表
    plt.show()


# todo 4、箱型图

'''通过调用 Series.box.plot() 、DataFrame.box.plot() 或者 DataFrame.boxplot() 方法来绘制箱型图，它将每一列数据的分布情况，以可视化的图像展现出来。
箱型图（也称为盒须图或盒图，英文为 Box Plot）是一种统计图表，用于展示数据集的分布情况。
它提供了一个五数概括（five-number summary），包括最小值、第一个四分位数（Q1）、中位数（Q2）、
第三个四分位数（Q3）和最大值，还可以帮助识别数据中的异常值（离群值）。

    组成部分
    
        1. 箱体（Box）：
           - 下边缘：第一个四分位数（Q1），即数据的下四分位数，表示数据中有 25% 的值小于或等于 Q1。
           - 上边缘：第三个四分位数（Q3），即数据的上四分位数，表示数据中有 75% 的值小于或等于 Q3。
           - 中间线：中位数（Q2），即数据的中位数，表示数据的中间值。
        
        2. 须（Whiskers）：
           - 须通常延伸到数据的最小值和最大值，但不包括异常值。具体地，它们通常延伸到 Q1 - 1.5*IQR 和 
             Q3 + 1.5*IQR 之间的范围，其中 IQR 是四分位距（Q3 - Q1）。
        
        3. 异常值（Outliers）：
           - 位于须之外的数据点，通常用点或其他符号表示。这些值被认为是异常值或离群值。
        
    示例
    
    假设我们有以下数据集：
    
        [7, 15, 36, 39, 40, 41, 42, 43, 47, 49]
    
    我们可以计算出：
    
        - 最小值：7
        - 第一个四分位数（Q1）：36
        - 中位数（Q2）：40.5
        - 第三个四分位数（Q3）：43
        - 最大值：49
    
    绘制箱型图
    
        import matplotlib.pyplot as plt
        
        data = [7, 15, 36, 39, 40, 41, 42, 43, 47, 49]
        
        plt.boxplot(data)
        plt.title("Box Plot Example")
        plt.ylabel("Values")
        plt.show()
    
    解释
        - 箱体：从 Q1 (36) 到 Q3 (43)，中间的线表示中位数 (40.5)。
        - 须：从最小值 (7) 到最大值 (49)。
        - 异常值：如果有任何数据点位于 Q1 - 1.5*IQR 或 Q3 + 1.5*IQR 之外，它们将被标记为异常值。
    
    用途
        箱型图广泛用于：
        - 比较多个数据集的分布。
        - 识别数据中的异常值。
        - 了解数据的对称性和偏态。
    
    总的来说，箱型图是一种简洁而有力的工具，用于可视化数据的分布和识别异常值。

Matplotlib 提供了许多方法和参数来定制你的图表。以下是一些常用的图表定制方法和参数：

    标题和标签
        - plt.title("标题"): 设置图表的标题。
        - plt.xlabel("X轴标签"): 设置 x 轴的标签。
        - plt.ylabel("Y轴标签"): 设置 y 轴的标签。
    
    轴范围
        - plt.xlim([xmin, xmax]): 设置 x 轴的范围。
        - plt.ylim([ymin, ymax]): 设置 y 轴的范围。
    
    网格
        - plt.grid(True): 显示网格。
        - plt.grid(False): 隐藏网格。
    
    图例
        - plt.legend(["标签1", "标签2"]): 添加图例。
    
    样式和颜色
        - plt.plot(x, y, color='red', linestyle='--', marker='o'): 设置线条颜色、线型和标记。
    
    其他
        - plt.xticks(ticks, labels): 设置 x 轴的刻度和标签。
        - plt.yticks(ticks, labels): 设置 y 轴的刻度和标签。
        - plt.savefig("filename.png"): 保存图表为文件。

'''


def pandas_box():
    df = pd.DataFrame(np.random.rand(10, 4), columns=['A', 'B', 'C', 'D'])
    df.plot.box()

    plt.show()

    data = [7, 15, 36, 39, 40, 41, 42, 43, 47, 49]

    # 设置字体为宋体
    # 设置全局字体为 STHeiti（看自己电脑有没有字体）
    plt.rcParams['font.sans-serif'] = ['STHeiti']
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
    # 示例数据
    data = [7, 15, 36, 39, 40, 41, 42, 43, 47, 49]

    # 绘制箱型图
    plt.boxplot(data)
    plt.title("箱型图示例")
    plt.xlabel("样本数据")
    plt.ylabel("值")
    plt.grid(True)
    plt.xticks([1], ["数据集1"])
    plt.ylim([0, 50])
    plt.show()


# todo 5、区域图

def pandas_scatter():
    '''使用 Series.plot.area() 或 DataFrame.plot.area() 方法来绘制区域图。'''
    df = pd.DataFrame(np.random.rand(5, 4), columns=['a', 'b', 'c', 'd'])

    # 指定每个区域的颜色（使用十六进制颜色代码）
    colors = ['#FF0000', '#00FF00', '#0000FF', '#FFFF00']

    # 绘制区域图并指定颜色
    df.plot.area(color=colors)

    # 显示图形
    plt.show()


# todo 6、散点图

def pandas_scatter_box():
    '''使用 DataFrame.plot.scatter() 方法来绘制散点图'''
    df = pd.DataFrame(np.random.rand(30, 4), columns=['a', 'b', 'c', 'd'])

    # 使用 'a' 列作为横坐标，'b' 列作为纵坐标绘制散点图，并设置颜色和标记样式
    ax = df.plot.scatter(x='a', y='b', color='red', marker='*')

    # 添加标题和轴标签
    ax.set_title('Scatter Plot of a vs b')
    ax.set_xlabel('a')
    ax.set_ylabel('b')

    # 显示图形
    plt.show()


# todo 7、饼状图
def pandas_pie():
    df = pd.DataFrame(3 * np.random.rand(4), index=['go', 'java', 'c++', 'c'], columns=['L'])

    # 设置全局字体为 STHeiti（看自己电脑有没有字体）
    plt.rcParams['font.sans-serif'] = ['STHeiti']
    plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题

    # 生成示例数据
    df = pd.DataFrame(3 * np.random.rand(4), index=['go', 'java', 'c++', 'c'], columns=['L'])

    # 绘制饼图并设置颜色
    df.plot.pie(
        subplots=True,  # 是否为每一列生成单独的子图
        autopct='%1.1f%%',  # 设置百分比显示格式，保留一位小数(
        # % 是格式化字符串的占位符，表示一个百分比值。
        # 1.1f 表示保留一位小数的浮点数，其中：
        # 1 表示至少有一个字符宽度（在这种情况下，由于百分比通常超过一个字符宽度，这个值的设置不是很重要）。
        # .1 表示保留一位小数。
        # f 表示浮点数。
        # 你可以根据需要调整这个格式字符串。例如：
        #
        # autopct='%1.0f%%' 会显示整数百分比，不保留小数。
        # autopct='%1.2f%%' 会显示两位小数的百分比。)

        colors=['red', 'green', 'blue', 'yellow'],  # 设置每个扇区的颜色
        startangle=90,
        # 设置饼图的起始角度为90度（默认情况下，Matplotlib 会从 0 度（即从正东方向）开始绘制饼图，但通过设置 startangle 参数，你可以改变这一起始角度。例如，startangle=90 表示从 90 度（即从正南方向）开始绘制饼图。）
        shadow=True,  # 为饼图添加阴影
        explode=[0.1, 0, 0, 0],  # 将第一个扇区稍微分离出来，偏移量为 0.1，而其他三个扇区不分离（偏移量为 0）。
        figsize=(8, 6),  # 设置图形的大小为8x6英寸
        title='编程语言使用饼状图',  # 设置图形的标题
        legend=True,  # 显示图例
        fontsize=12,  # 设置标签和标题的字体大小为12
        frame=True,  # 绘制图形边框
        pctdistance=0.7,  # 设置百分比标签与饼图中心的距离为0.7
        labeldistance=1.2,  # 设置标签与饼图中心的距离为1.2
        center=(0.5, 0.5),  # 设置饼图中心的位置
        radius=1.2,  # 设置饼图的半径为1.2
        counterclock=False,  # 顺时针绘制饼图
        wedgeprops={'edgecolor': 'black'},  # 设置每个扇区的边框颜色为黑色
        textprops={'color': 'black'}  # 设置文本标签的颜色为黑色
    )

    # 设置标题字体
    plt.title('编程语言使用情况')

    # 显示图形
    plt.show()


def main():
    # pandas_cahrt_demo()
    # pandas_bar()
    # pandas_hist()
    # pandas_box()
    # pandas_scatter()
    # pandas_scatter_box()
    pandas_pie()


if __name__ == '__main__':
    main()
